// created on 22.07.2007 at 17:06


using System;
using System.Collections;
using System.Collections.Generic;
using Gtk;
using System.IO;
using System.Xml;
using ICSharpCode.SharpZipLib.Zip;


namespace Designer
{
	public partial class ResourceMgr
	{
		void WriteZipEntry(ZipOutputStream zip, string file, string target)
		{
			FileStream fs = File.OpenRead(file);
			byte[] buffer = new byte[fs.Length];
			fs.Read(buffer, 0, buffer.Length); 
			ZipEntry entry = new ZipEntry(target);
			zip.PutNextEntry(entry);
			zip.Write(buffer, 0, buffer.Length);
			fs.Close();
		}
		void ReadZipEntry(ZipInputStream zip, string target)
		{
			FileStream sw = File.Create((target));
			int nBytes = 2048;
			byte[] data = new byte[2048];
			while((nBytes = zip.Read(data, 0, data.Length))  > 0)
			{
				sw.Write(data,0,nBytes);
			}
			sw.Close();
		}
		
		public void SaveProject(string fname)
		{	      
			Console.WriteLine("Save project to file "+fname);
			XmlTextWriter textWriter = new XmlTextWriter(tempDir+"game.xml", null);
			textWriter.WriteStartDocument();
			textWriter.WriteStartElement("GamadusProject");
			textWriter.WriteAttributeString("version", VERSION);
			textWriter.WriteStartElement("Resources");
			//Durchläuft alle Ressourcen und schreibt sie in die Datei
			foreach(Resource res in list.Values)
			{
				//Resource res = (Resource) myEntry.Value;
				textWriter.WriteStartElement(res.GetResType());
				textWriter.WriteAttributeString("name",res.Name);
				//Durchläuft alle Settings der Ressource und schreibt sie in die Datei
				foreach(DictionaryEntry mySetting in res.GetSettings())
				{
					if(mySetting.Value == null)
						continue;
					textWriter.WriteStartElement("Set");
					textWriter.WriteAttributeString("name",mySetting.Key.ToString());
					if(!WriteXmlValue(textWriter, mySetting.Value))
						textWriter.WriteString(mySetting.Value.ToString());

					textWriter.WriteEndElement(); //Set
				}
				//Wenn die Ressource ein Objekt ist, schreibe objektspezifische Daten
				if(res.GetResType() == "Object")
				{
					Obj obj = (Obj) res;
					//Durchlaufe alle Events im Objekt
					foreach(Event ev in obj.events)
					{
						textWriter.WriteStartElement("Event");
						textWriter.WriteAttributeString("id",ev.Name);
						//Durchlaufe alle Actions im Event
						foreach(Designer.Action act in ev.actions)
						{
							textWriter.WriteStartElement("Action");
							textWriter.WriteAttributeString("set",act.Set.name);
							textWriter.WriteAttributeString("id",act.Type);
							//Durchlaufe alle Argumente der Action
							foreach(ArrayList arg in act.Args)
							{
								WriteActionArgs(textWriter, arg);
							}
							textWriter.WriteEndElement(); //Action
						}
						textWriter.WriteEndElement(); //Event
					}
				}
				else if(res.GetResType() == "Room")
				{
					Room lev = (Room) res;
					//Durchlaufen aller Instanzen in der Ebene
					foreach(Inst inst in lev.instances)
					{
						textWriter.WriteStartElement("Instance");
						textWriter.WriteAttributeString("x", inst.x.ToString());
						textWriter.WriteAttributeString("y", inst.y.ToString());
						WriteXmlValue(textWriter, inst.obj);
						textWriter.WriteEndElement(); //Instance
					}
				}
				textWriter.WriteEndElement(); //Single Resource
			}
			textWriter.WriteEndElement(); // Resources
			textWriter.WriteStartElement("Settings");
			foreach(DictionaryEntry setting in GameSettings)
			{
				textWriter.WriteStartElement("Set");
				textWriter.WriteAttributeString("name", (string)setting.Key);
				this.WriteXmlValue(textWriter, setting.Value);
				textWriter.WriteEndElement(); //Set
			}
			textWriter.WriteEndElement(); // Settings
			textWriter.WriteEndElement(); //End project
			textWriter.WriteEndDocument();
			textWriter.Close();
			XmlDocument doc = new XmlDocument();
			StreamReader tmpr = new StreamReader(tempDir+"game.xml");
			doc.Load(tmpr);
			tmpr.Close();
			StreamWriter tmpw = new StreamWriter(tempDir+"game.xml");
			//Save the document to a file.
			doc.Save(tmpw);
			tmpw.Close();
			
			ZipOutputStream zip = new ZipOutputStream(new StreamWriter(fname).BaseStream);
			WriteZipEntry(zip,tempDir+"game.xml","game.xml");
			
			foreach(FileResource fr in FileResourceMgr.Instance.Resources)
			{
				if(!File.Exists(tempDir+fr.TempFile))
					throw new FileNotFoundException("temp resource file not found");
				WriteZipEntry(zip,tempDir+fr.TempFile,fr.TempFile);
			}
			zip.Finish();
			zip.Close();
			File.Delete(tempDir+"game.xml");
		}
		public void LoadProject(string fname)
		{
			NewProject();
			Console.WriteLine("Open project from file "+fname);
			
			ZipInputStream zip = new ZipInputStream(new StreamReader(fname).BaseStream);
			ZipEntry curEntry;
			while ((curEntry = zip.GetNextEntry()) != null) 
		    {
				Console.WriteLine("Zip entry found: "+curEntry.Name);
				if(curEntry.Name == "game.xml")
				{
					ReadZipEntry(zip,tempDir+"game.xml");
				}
				else
				{
					ReadZipEntry(zip,tempDir+curEntry.Name);
				}
		    }
			zip.Close();
			
			XmlDocument doc = new XmlDocument();
        	doc.Load(tempDir+"game.xml");
        	//LOAD OBJECTS
        	XmlNodeList project = doc.GetElementsByTagName("GamadusProject");
        	if(project.Count != 1)
        	{
        		throw new ArgumentException(fname+" is not a valid Gamadus Project File");
        	}
        	int[] version;
        	if(project[0].Attributes.Count < 1)
        	{
        		Console.WriteLine("Project doesn't contain version information...");
        		version = ParseVersion("0.1.0");
        	}
        	else
        	{
        		string ver = project[0].Attributes[0].InnerText;
        		version = ParseVersion(ver);
        	}
        	Console.WriteLine("Gamadus Version: "+VERSION+"; Project Version: "+version[0]+"."+version[1]+"."+version[2]);
        	int[] gver = ParseVersion(VERSION);
        	if(version[0] > gver[0] || (version[0] == gver[0] && version[1] > gver[1]) || (version[0] == gver[0] && version[1] == gver[1] && version[2] > gver[2]))
        	{
        		Gtk.MessageDialog message = new MessageDialog(Win, DialogFlags.Modal, MessageType.Error, ButtonsType.None, false, "");
				message.Text = "Das Projekt wurde mit einer neueren Version von Gamadus ("+version[0]+"."+version[1]+"."+version[2]+") erstellt und kann deshalb nicht geöffnet werden. Sie verwenden Gamadus "+VERSION;
				message.Title = "Veraltete Version";
				message.AddButton(Stock.Ok, ResponseType.Ok);
				ResponseType RetVal = (ResponseType)message.Run();
				message.Destroy();
				return;
        	}
        	if(doc.GetElementsByTagName("Resources").Count < 1)
        		throw new ArgumentException(fname+" doesn't contain a Resources-Tag. Can't load file");
        	XmlNode resNode = doc.GetElementsByTagName("Resources")[0];
        	//TreeIter addIter;
        	List<ArrayList> ResourceList = new List<ArrayList>();
			foreach(XmlNode node in resNode.ChildNodes)
			{
				Resource r = null;
				//Console.WriteLine("Resource found: "+node.Name);
				switch(node.Name)
				{
				case "Object":
					Obj obj = new Obj(node.Attributes["name"].InnerText);
					foreach(XmlNode child in node.ChildNodes)
					{
						if(child.Name == "Set")
						{
							obj.SetSettings(child, ResourceList);
						}
						else if(child.Name == "Event")
						{
							Event ev = new Event(Event.StringType(child.Attributes["id"].InnerText),obj);
							obj.events.Add(ev);
							foreach(XmlNode actNode in child.ChildNodes)
							{
								if(actNode.Name == "Action")
								{
									Action act = new Action(actNode.Attributes["id"].InnerText, Designer.Action.GetLocation(obj, ev), this.GetActionSetByName(actNode.Attributes["set"].InnerText));
									ReadActionArgs(actNode, act);
									ev.actions.Add(act);
								}
							}
						}
					}
					r = obj;
				break;
				case "Sprite":
					r = new Gra(node.Attributes["name"].InnerText);
				break;
				case "Background":
					r = new Back(node.Attributes["name"].InnerText);
				break;
				case "Room":
					Room lev = new Room(node.Attributes["name"].InnerText);
					foreach(XmlNode child in node.ChildNodes)
					{
						if(child.Name == "Set")
						{
							lev.SetSettings(child, ResourceList);
						}
						else if(child.Name == "Instance")
						{
							Console.WriteLine("Instance found");
							Inst inst = new Inst(null, int.Parse(child.Attributes["x"].InnerText), int.Parse(child.Attributes["y"].InnerText));
							object Value = ReadXmlValue(child.ChildNodes[0], true);
							if(Value is ArrayList)
							{
								ArrayList arrlist = (ArrayList) Value;
								arrlist.Add(inst);
								ResourceList.Add(arrlist);
								lev.instances.Add(inst);
							}
						}
					}
					r = lev;
				break;
				case "Font":
					Font font = new Font(node.Attributes["name"].InnerText);
					foreach(XmlNode child in node.ChildNodes)
					{
						if(child.Name == "Set")
						{
							font.SetSettings(child, ResourceList);
						}
					}
					r = font;
				break;
				}
				TreeIter addIter = store.AppendValues(Win.ResGroups[node.Name],r.Name);
				if(r is Obj)
				{
					foreach(Event e in (r as Obj).events)
					{
						ResourceMgr.Instance.store.AppendValues(addIter, e.Name);
					}
				}
				list.Add(addIter, r);
			}
			
			if(doc.GetElementsByTagName("Settings").Count < 1)
        		throw new ArgumentException(fname+" doesn't contain a Settings-Tag. Can't load file");
        	XmlNode setNode = doc.GetElementsByTagName("Settings")[0];
        	//Durchlaufe alle Tags unter Settings
        	foreach(XmlNode node in setNode.ChildNodes)
			{
				//Gehe zum nächsten eintrag, wenn es kein Set-Tag ist
				if(node.Name != "Set") continue;
				
				string key = node.Attributes["name"].InnerText;
				if(node.ChildNodes.Count > 0)
					GameSettings[key] = ReadXmlValue(node.ChildNodes[0], false);
				else
					GameSettings[key] = node.InnerText; 
			}
			
			foreach(ArrayList arr in ResourceList)
			{
				string type = (string) arr[0];
				string name = (string) arr[1];
				if(arr[2] is Inst)
				{
					Inst inst = (Inst) arr[2];
					inst.obj =  (Obj)GetResourceByName(type, name);
				}
				else if(arr[2] is Resource && arr[3] is String)
				{
					Resource res = (Resource) arr[2];
					string key = (string) arr[3];
					res.Settings[key] = GetResourceByName(type, name);
				}
			}
			File.Delete(tempDir+"game.xml");
		}
		void WriteActionArgs(XmlTextWriter writer, ArrayList args)
		{
			string type = (string) args[0];
			string val;
			if(type == "Code")		
				val =((Code)args[1]).code;
			else
				val = (string) args[1];
				
			writer.WriteStartElement("Arg");
			writer.WriteAttributeString("type",type);
			if(type == "Code")
				writer.WriteAttributeString("lang", ((Code)args[1]).language);
			writer.WriteString(val);
			writer.WriteEndElement();
		}
		void ReadActionArgs(XmlNode node, Designer.Action act)
		{
			foreach(XmlNode arg in node.ChildNodes)
			{
				if(arg.Name == "Arg")
				{
					ArrayList list = new ArrayList();
					list.Add(arg.Attributes["type"].InnerText);
					if((string)list[0] == "Code")
					{
						list.Add(new Code(arg.InnerText, act.Location));
					}
					else
						list.Add(arg.InnerText);
					act.Args.Add(list);
				}
			}
		}
		public bool NewProject()
		{
			if(Win.editTabsList.Count > 0)
			{
				
				Gtk.MessageDialog message = new MessageDialog(Win, DialogFlags.Modal, MessageType.Question, ButtonsType.None, false, "");
				message.Text = "Es sind noch Editiertabs offen. Sollen die Änderungen darin übernommen werden?";
				message.Title = "Änderungen übernehmen?";
				message.AddButton(Stock.Yes, ResponseType.Yes);
				message.AddButton(Stock.No, ResponseType.No);
				message.AddButton(Stock.Cancel, ResponseType.Cancel);
				ResponseType RetVal = (ResponseType)message.Run();
				message.Destroy();
				if(RetVal == ResponseType.Yes || RetVal == ResponseType.No)
				{
					List<Resource> closeList = new List<Resource>();
					foreach(KeyValuePair<Resource,iEditTab> tabEntry in Win.editTabsList)
					{
						closeList.Add((Resource) tabEntry.Key);
					}
					foreach(Resource Res in closeList)
					{
						Win.CloseEditTab(Res, (RetVal == ResponseType.Yes));
					}
				}
				else if(RetVal == ResponseType.Cancel)
				{
					return false;
				}
			}
			//if there are unsaved changes in the projekt, ask if want to save project
			if(Win.unsavedChanges)
			{
				Gtk.MessageDialog message = new MessageDialog(Win, DialogFlags.Modal, MessageType.Question, ButtonsType.None, false, "");
				message.Text = "Es liegen ungespeicherte Änderungen vor, wollen Sie das Projekt speichern?";
				message.Title = "Projekt speichern?";
				message.AddButton(Stock.Cancel, ResponseType.Cancel);
				message.AddButton(Stock.No, ResponseType.No);
				message.AddButton(Stock.Yes, ResponseType.Yes);
				ResponseType RetVal = (ResponseType)message.Run();
				message.Destroy();
				if(RetVal == ResponseType.Yes)
					Win.Save(false);
				else if(RetVal == ResponseType.Cancel)
					return false;
			}
			//reset project name and unsaved changes
			Win.projectName = "Neues Projekt";
			Win.unsavedChanges = false;
			//clear the resource tree and add group items again
			store.Clear();
			Win.ResGroups["Sprite"] = store.AppendValues ("Grafiken");
			Win.ResGroups["Background"] = store.AppendValues ("Hintergründe");
			Win.ResGroups["Object"] = store.AppendValues ("Objekte");
			Win.ResGroups["Room"] = store.AppendValues ("Ebenen");
			Win.ResGroups["Classe"] = store.AppendValues("Klassen");
			Win.ResGroups["Font"] = store.AppendValues("Schriften");
			list.Clear();
			FileResourceMgr.Instance.Clean();
			return true;
		}
		int[] ParseVersion(string version)
		{
			int[] v = new int[3];
			int pos = version.IndexOf('.');
			v[0] = int.Parse(version.Substring(0,pos));
			version = version.Substring(pos+1);
			pos = version.IndexOf('.');
			v[1] = int.Parse(version.Substring(0,pos));
			version = version.Substring(pos+1);
			v[2] = int.Parse(version);
			return v;
        }
	}
}